package __4_0

/*
#cgo CFLAGS	: -I./include
#cgo LDFLAGS: -L${SRCDIR}/lib -larcsoft_face_engine
#include <stdlib.h>
#include "./include/merror.h"
#include "./include/asvloffscreen.h"
#include "./include/arcsoft_face_sdk.h"
*/
import "C"

import (
	"arc_srv4/common"
	"arc_srv4/database"
	"arc_srv4/logutils"
	"arc_srv4/utils"
	"bytes"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"go.uber.org/zap"
	"image"
	"image/jpeg"
	"io/ioutil"
	"strconv"
)

// base64编码转byte
func ImageByte(face, transId string) ([]byte, string) {
	imageByte, err := base64.StdEncoding.DecodeString(face)

	if err != nil {
		logutils.Logger.Error("人脸数据不正确", zap.String("transId", transId), zap.String("error", err.Error()))
		return nil, "人脸数据不正确"
	}
	return imageByte, "成功"
}

// 保存图片
func SaveImage(path, name string, imageByte []byte, transId string) (bool, string) {
	utils.CreatePath(path)
	fileName := path + name
	err := ioutil.WriteFile(fileName, imageByte, 0666)
	if err != nil {
		logutils.Logger.Error("保存图片失败", zap.String("transId", transId), zap.String("error", err.Error()))
		return false, "保存图片失败"
	}
	return true, "成功"
}

// 图片转化
func ImageConvert(imageByte []byte, transId string) (width, height int, bgrData []uint8) {
	imgReader := bytes.NewReader(imageByte)
	imgReq, _, err := image.Decode(imgReader)
	if err != nil {
		logutils.Logger.Error("图片转化失败", zap.String("transId", transId), zap.String("error", err.Error()))
		return 0, 0, nil
	}

	width, height = utils.GetImageSize(imgReq)
	bgrData = utils.GetResizedBGRFromByte(imageByte)
	return
}

// 从文件进行图片转化
func ImageConvertFromImage(fileName string) (width, height int, bgrData []uint8, bb []byte) {
	width, height = utils.GetImageWidthAndHeight(fileName)
	bgrData = utils.GetResizedBGR(fileName)
	bb = utils.GetImageByte(fileName)
	return
}

// 激活
func ActivationClient(appId, appKey, activeKey string) bool {

	cpu, mac := utils.GetCpuId(), utils.GetMacAddrs()[0]
	// 检查当年是否已经激活
	strSql := fmt.Sprintf("select a.appId,a.appKey,a.activeKey,a.activation_time "+
		"from t_api_register a inner join t_api_info b on a.appKey = b.appKey and a.appId = b.appId "+
		"where a.appId =\"%s\" and a.appKey =\"%s\" and cpu = \"%s\" and mac = \"%s\" and a.activation_time < b.expire_time "+
		"order by register_time desc limit 0,1", appId, appKey, cpu, mac)
	//println(strSql)
	res, err := database.Db.Engine.QueryString(strSql)
	if err != nil {
		logutils.Logger.Error("query t_api_register error.",
			zap.String("appId", appId),
			zap.String("appKey", appKey),
			zap.String("activeKey", activeKey),
			zap.String("error", err.Error()),
		)
		return false
	}

	if len(res) > 0 {
		logutils.Logger.Info("你的账户已经激活，无需再次激活。", zap.String("appId", appId), zap.String("appKey", appKey))
		return true
	}

	// 专业版激活
	err = OnlineActivationPro(appId, appKey, activeKey)
	if err != nil {
		logutils.Logger.Error(err.Error(), zap.String("appId", appId), zap.String("appKey", appKey), zap.String("appKey", activeKey))
		return false
	}
	// 插入激活记录
	strSql = fmt.Sprintf("insert into t_api_register(cpu,mac,appId,appKey,activeKey,activation_time,create_time)"+
		"values(\"%s\",\"%s\",\"%s\",\"%s\",\"%s\",now(),now())", cpu, mac, appId, appKey, activeKey)
	_, _ = database.Db.Engine.Exec(strSql)

	logutils.Logger.Info("你的账户已经激活，请放心使用。",
		zap.String("appId", appId),
		zap.String("appKey", appKey),
	)
	return true
}

// 解析错误原因
func explainError(a int) string {
	switch a {
	case common.ARC_ACTIVATE_28673:
		return common.ARC_ACTIVATE_28673_Desc
	case common.ARC_ACTIVATE_28674:
		return common.ARC_ACTIVATE_28674_Desc
	case common.ARC_ACTIVATE_28676:
		return common.ARC_ACTIVATE_28676_Desc
	default:
		return ""
	}
}

// 初始化引擎
func InitEngineUtil(transId string) (bool, *FaceEngine) {
	engine, err := NewFaceEngine(DetectModeImage,
		OrientPriority0,
		10,
		EnableFaceDetect|EnableFaceRecognition|EnableFace3DAngle|EnableLiveness|EnableIRLiveness|EnableAge|
			EnableGender|EnableImageQuality|EnableUpdateFaceData|EnableFaceShelter|EnableMaskDetect|EnableFaceLandMark,
	)
	if err != nil {
		logutils.Logger.Error("初始化引擎失败.", zap.String("transId", transId), zap.String("error", err.Error()))
		return false, nil
	}
	logutils.Logger.Info("初始化引擎成功.", zap.String("transId", transId))
	return true, engine
}

// 销毁引擎
func ReleaseEngine(engine *FaceEngine, transId string) bool {
	if engine != nil {
		if err := engine.DestroyPro(); err != nil {
			logutils.Logger.Error("销毁引擎失败.", zap.String("transId", transId), zap.String("error", err.Error()))
			return false
		}
		logutils.Logger.Info("销毁引擎成功.", zap.String("transId", transId))
		return true
	}
	logutils.Logger.Info("销毁引擎无需销毁.", zap.String("transId", transId))
	return true
}

// 检测人脸
func DectFaceUtil(engine *FaceEngine, transId string, width, height int, bgrData []uint8) *MultiFaceInfo {
	// 检测人脸
	faceInfo, err := engine.DetectFacesPro(width-width%4, height, ColorFormatBGR24, bgrData)
	if err != nil {
		ReleaseEngine(engine, transId)
		logutils.Logger.Error("检测人脸失败.", zap.String("transId", transId), zap.Any("error", err))
		return nil
	}
	logutils.Logger.Info("检测人脸成功.", zap.String("transId", transId))
	return &faceInfo
}

// 比对照片质量
func qualityCheck(engine *FaceEngine, transId string, width, height int, bgrData []uint8, single SingleFaceInfo, isMask int) float32 {
	result, err := engine.ASFImageQualityDetectPro(width-width%4, height, ColorFormatBGR24, bgrData, single, isMask)
	if err != nil {
		ReleaseEngine(engine, transId)
		logutils.Logger.Error("更新人脸信息失败.", zap.String("transId", transId), zap.String("error", err.Error()))
		return -1
	}
	return result
}

// 提取特征码
func FeatureExtractUtil(engine *FaceEngine, transId string, width, height int, bgrData []uint8, single SingleFaceInfo, isReg, mask uint32) (ib bool, ff FaceFeature) {

	// 默认注册照
	isRegOrNot := ASFRegister
	if isReg == 1 {
		// 识别照
		isRegOrNot = ASFRecognition
	}
	ff, err := engine.FaceFeatureExtractPro(width-width%4, height, ColorFormatBGR24, bgrData, single, isRegOrNot, mask)
	if err != nil {
		ib = false
		ReleaseEngine(engine, transId)
		logutils.Logger.Error("提取单个人脸特征值失败.", zap.String("transId", transId), zap.String("error", err.Error()))
		return
	}
	ib = true
	logutils.Logger.Info("提取单个人脸特征值成功.", zap.String("transId", transId))
	return
}

// 比对人脸特征
func CompareFaceUtil(engine *FaceEngine, transId string, feature1, feature2 FaceFeature, compareModel int32) float32 {
	// 新增参数比对方式
	// 默认生活照
	mods := ASFLifePhoto
	if compareModel == 1 {
		mods = ASFIdPhoto
	}
	result, err := engine.FaceFeatureComparePro(feature1, feature2, int32(mods))
	if err != nil {
		ReleaseEngine(engine, transId)
		logutils.Logger.Error("比对单个人脸特征值失败.", zap.String("transId", transId), zap.String("error", err.Error()))
		return -1
	}
	logutils.Logger.Info("比对单个人脸特征值成功.", zap.String("transId", transId), zap.Float32("相似度", result))
	return result
}

//

// 查询归属设备党支部人脸数据
func QueryFaces(deviceNo, transId string) []map[string]string {
	// 提取数据库信息
	sql := fmt.Sprintf("select readerId,readerName,photo,face_data,valid,modifyTime "+
		"from t_user_photo photo "+
		"left join t_branch_equipment eq on photo.branchCode =eq.branch_code "+
		"where eq.equipment_id ='%s' and valid=1 order by photo.modifyTime desc ", deviceNo)

	res, err := database.Db.Engine.QueryString(sql)
	if err != nil {
		logutils.Logger.Error("查询人脸数据失败.", zap.String("transId", transId), zap.String("error", err.Error()))
		return nil
	}
	return res
}

// 新增人脸数据
func AddNeWFace(readerId, face, photo, transId string) bool {
	// 提取数据库信息
	sql := fmt.Sprintf("insert into t_user_photo (readerId,readerName,photo,modifyTime,valid,branchId,branchCode,face_data) "+
		"select '%s',info.fullname,'%s',now(),1,info.branchId,info.branchCode,'%s' from t_user_info info where reader_id='%s'",
		readerId, photo, face, readerId)

	affect, err := database.Db.Engine.Exec(sql)
	if err != nil {
		logutils.Logger.Error("绑定人脸失败.", zap.String("transId", transId), zap.String("error", err.Error()))
		return false
	}
	num, err := affect.RowsAffected()
	if err != nil {
		logutils.Logger.Error("绑定人脸失败.", zap.String("transId", transId), zap.Int64("affected row", num), zap.String("error", err.Error()))
		return false
	}
	logutils.Logger.Info("绑定人脸成功.", zap.String("transId", transId), zap.Int64("affected row", num))

	//查询刚插入的数据
	sqlQuery := fmt.Sprintf(`SELECT photo.id as id,info.fullname,photo.valid FROM t_user_photo photo
									join t_user_info info on photo.readerId = info.reader_id
									where readerId = '%s'
									and face_data = '%s'
									order by id desc limit 1`, readerId, face)

	QueryResult, err := database.Db.Engine.QueryString(sqlQuery)
	if err != nil {
		logutils.Logger.Error("查询新增人脸id失败.", zap.String("error", err.Error()))
	}
	if (len(QueryResult)) <= 0 {
		logutils.Logger.Error("查询新增人脸id失败.")
		return false
	}
	//新增人脸存入redis
	faceData := new(common.FaceData)
	faceData.Id = QueryResult[0]["id"]
	faceData.ReaderId = readerId
	faceData.ReaderName = QueryResult[0]["fullname"]
	faceData.Photo = face
	faceData.Valid = utils.StringToBool(QueryResult[0]["valid"])
	value, _ := json.Marshal(faceData)
	utils.RedisSet("face_"+QueryResult[0]["id"], string(value), -1)

	sqlQuery = fmt.Sprintf("select id from t_user_photo where readerId = '%s' order by id desc limit 3,1 ", readerId)
	QueryResult = nil
	QueryResult, err = database.Db.Engine.QueryString(sqlQuery)
	var keys []interface{}
	if len(QueryResult) > 0 {
		for _, v := range QueryResult {
			keys = append(keys, "face_"+v["id"])
		}
	}
	err = utils.RedisDelete(keys)
	if err != nil {
		logutils.Logger.Error("删除redis多余人脸失败.", zap.String("error", err.Error()))
	}

	// 每个人只保留10张人脸
	sqlDel := fmt.Sprintf("delete a from t_user_photo as a inner join ( "+
		"select id,readerId,photo from t_user_photo where readerId = '%s' order by id desc limit 3,1 "+
		")b on a.readerId = b.readerId and a.photo = b.photo and a.id <= b.id ", readerId)

	delAf, err := database.Db.Engine.Exec(sqlDel)
	if err != nil {
		logutils.Logger.Error("删除多余人脸失败.", zap.String("transId", transId), zap.String("error", err.Error()))
		return false
	}
	num, err = delAf.RowsAffected()
	if err != nil {
		logutils.Logger.Error("删除多余人脸失败.", zap.String("transId", transId), zap.Int64("affected row", num), zap.String("error", err.Error()))
		return false
	}
	if num > 0 {
		logutils.Logger.Info("删除多余人脸成功.", zap.String("transId", transId), zap.Int64("affected row", num))
	}

	logutils.Logger.Info("新增人脸成功.", zap.String("transId", transId))
	return true
}

// 单个人脸信息
func NewSingleFace(info *MultiFaceInfo) SingleFaceInfo {
	if info.FaceNum <= 0 {
		return SingleFaceInfo{}
	}
	single := SingleFaceInfo{}
	single.FaceRect = info.FaceRect[0]
	single.FaceOrient = info.FaceOrient[0]
	single.DataInfo = info.FaceDataInfoList[0]
	return single
}

// 释放内存
func ReleaseFeature(ff FaceFeature) {
	ff.Release()
}

// 设置遮挡范围
func SetShelterUtil(engine *FaceEngine, transId string, threshold float32) bool {
	err := engine.SetFaceShelterParamPro(threshold)
	if err != nil {
		logutils.Logger.Error("设置遮挡范围失败.", zap.String("transId", transId), zap.String("error", err.Error()))
		return false
	}
	logutils.Logger.Info("设置遮挡范围成功.", zap.String("transId", transId))
	return true
}

// 设置活体置信度
func SetLiveUtil(engine *FaceEngine, transId string, rgb, iir float32) bool {
	lv := LivenessThreshold{rgb, iir}
	err := engine.SetLivenessParamPro(lv)
	if err != nil {
		logutils.Logger.Error("设置活体置信度失败.", zap.String("transId", transId), zap.String("error", err.Error()))
		return false
	}
	logutils.Logger.Info("设置活体置信度成功.", zap.String("transId", transId))
	return true
}

// 获取人脸信息
func GetFaceInfos(engine *FaceEngine, transId string, width, height int, imageData []uint8, info MultiFaceInfo, img []byte, isReg, isMask int32) (
	ib bool, multi []*SingleFacePropertyFourth) {
	if err := engine.ProcessPro(width-width%4, height, ColorFormatBGR24, imageData, info,
		EnableAge|EnableGender|EnableFace3DAngle|EnableLiveness|EnableMaskDetect|EnableFaceLandMark,
	); err != nil {
		logutils.Logger.Error("图片活体检测失败.", zap.String("transId", transId), zap.String("error", err.Error()))
		return
	}
	logutils.Logger.Info("图片活体检测成功.", zap.String("transId", transId))

	var faceProperty []*SingleFacePropertyFourth
	// 获取年龄
	ageInfo, err := engine.GetAgePro()
	if err != nil {
		logutils.Logger.Error("获取年龄失败.", zap.String("transId", transId), zap.String("error", err.Error()))
		return false, nil
	}
	logutils.Logger.Info("获取年龄成功.", zap.String("transId", transId))

	// 获取性别
	genderInfo, err := engine.GetGenderPro()
	if err != nil {
		logutils.Logger.Error("获取性别失败.", zap.String("transId", transId), zap.String("error", err.Error()))
		return false, nil
	}
	logutils.Logger.Info("获取性别成功.", zap.String("transId", transId))

	// 获取3DAngle
	angleInfo, err := engine.GetFace3DAnglePro()
	if err != nil {
		logutils.Logger.Error("获取3DAngle失败.", zap.String("transId", transId), zap.String("error", err.Error()))
		return false, nil
	}
	logutils.Logger.Info("获取3DAngle成功.", zap.String("transId", transId))

	// 获取口罩遮挡
	maskInfo, err := engine.GetMaskPro()
	if err != nil {
		logutils.Logger.Error("获取人脸遮挡失败.", zap.String("transId", transId), zap.String("error", err.Error()))
		return false, nil
	}
	logutils.Logger.Info("获取人脸遮挡成功.", zap.String("transId", transId))

	// 额头点位
	landMark, err := engine.GetFaceLandMarkInfo()
	if err != nil {
		logutils.Logger.Error("获取人脸额头失败.", zap.String("transId", transId), zap.String("error", err.Error()))
		return false, nil
	}
	logutils.Logger.Info("获取人脸额头成功.", zap.String("transId", transId))

	//获取活体值
	liveInfo, err := engine.GetLivenessScorePro()
	if err != nil {
		logutils.Logger.Error("获取活体值失败.", zap.String("transId", transId), zap.String("error", err.Error()))
		return false, nil
	}
	logutils.Logger.Info("获取活体值成功.", zap.String("transId", transId))

	maxInt := GetMaxFace(info.FaceRect)
	for k, v := range info.FaceRect {
		property := new(SingleFacePropertyFourth)
		property.FaceRect = v
		property.FaceOrient = info.FaceOrient[k]
		property.Age = ageInfo.AgeArray[k]
		property.Gender = genderInfo.GenderArray[k]
		property.GenderType = ExplainAge(genderInfo.GenderArray[k])
		property.Face3DAngle = angleInfo
		property.MaskInfo = maskInfo
		property.LandMarkInfo = landMark
		property.Live = liveInfo.IsLive[k]

		// 是否带眼镜
		property.WearGlasses = info.WearGlasses[k]
		property.LeftEyeClosed = info.LeftEyeClosed[k]
		property.RightEyeClosed = info.RightEyeClosed[k]

		// 提取人脸图片并以base64返回
		resImage := CutImage(img, v)
		// 转存提取的人脸
		SaveImage("./attachment/pics/slice/"+transId+"/", "face_"+transId+"_"+strconv.Itoa(k)+".jpg", resImage, transId)
		property.FaceImage = base64.StdEncoding.EncodeToString(resImage)

		// 提取活体值
		//pw,ph,imageD := ImageConvert(resImage,transId)
		//liveInfo := GetLive(transId,pw,ph,imageD)
		//if liveInfo != nil{
		//	property.Live = liveInfo.IsLive
		//	property.LiveType = ExplainLive(liveInfo.IsLive)
		//}

		// 提取人脸特征
		sing := SingleFaceInfo{v, info.FaceOrient[k], info.FaceDataInfoList[k]}
		_, feature := FeatureExtractUtil(engine, transId, width, height, imageData, sing, uint32(isReg), uint32(isMask))
		property.Feature = feature
		feature.Release()

		if maxInt == k {
			property.Max = 1
		}
		faceProperty = append(faceProperty, property)
	}
	return true, faceProperty
}

// 人脸活体检测
func GetLive(transId string, width, height int, imageData []uint8) *common.LiveInfo {
	ic, engine := InitEngineUtil(transId)
	if !ic {
		return nil
	}

	info := DectFaceUtil(engine, transId, width, height, imageData)
	if info == nil {
		return nil
	}

	if info.FaceNum == 0 {
		return nil
	}

	if err := engine.ProcessPro(width-width%4, height, ColorFormatBGR24, imageData, *info,
		EnableAge|EnableGender|EnableFace3DAngle|EnableLiveness|EnableMaskDetect|EnableFaceLandMark); err != nil {
		logutils.Logger.Error("活体检测-处理人脸数据失败.", zap.String("transId", transId), zap.String("error", err.Error()))
		return nil
	}
	liveInfo, err := engine.GetLivenessScorePro()
	if err != nil {
		logutils.Logger.Error("获取活体值失败.", zap.String("transId", transId), zap.String("error", err.Error()))
		return nil
	}
	logutils.Logger.Info("获取活体值成功.", zap.String("transId", transId))

	if liveInfo.Num > 0 {
		lv := new(common.LiveInfo)
		lv.IsLive = liveInfo.IsLive[0]
		return lv
	} else {
		return nil
	}

}

// 获取最大人脸
func GetMaxFace(rect []Rect) int {
	var maxFace int32 = 0
	maxInt := 0
	for k, v := range rect {
		tmpMax := (v.Right - v.Left) * (v.Bottom - v.Top)
		if tmpMax > maxFace {
			maxFace = tmpMax
			maxInt = k
		}
	}
	return maxInt
}

// 解释性别
func ExplainAge(i int32) string {
	switch i {
	case 0:
		return "男"
	case 1:
		return "女"
	case -1:
		return "不确定"
	default:
		return ""
	}
}

// 解释活体值
func ExplainLive(i int32) string {
	//0:非真人 1:真人 -1:不确定 -2:传入人脸数>1
	switch i {
	case 0:
		return "非真人"
	case 1:
		return "真人"
	case -1:
		return "不确定"
	case -2:
		return "传入人脸数大于1张"
	default:
		return ""
	}
}

// 裁剪图片
func CutImage(img []byte, rect Rect) []byte {
	// 裁剪
	buf := bytes.NewBuffer(img)
	m, _, _ := image.Decode(buf) // 图片文件解码
	rgbImg := m.(*image.YCbCr)
	subImg := rgbImg.SubImage(image.Rect(int(rect.Left), int(rect.Top), int(rect.Right), int(rect.Bottom))).(*image.YCbCr) //图片裁剪x0 y0 x1 y1

	// Image.image 转 byte
	buf2 := new(bytes.Buffer)
	_ = jpeg.Encode(buf2, subImg, nil)
	return buf2.Bytes()
}
